// Boost.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/scoped_ptr.hpp>
#include <boost/filesystem.hpp> 
#include <boost/test/unit_test.hpp>
#include <boost/thread.hpp>

#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
#include <boost/functional/hash.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/function/function2.hpp>

#include <string>

#include <set>
#include <utility>
#include <functional>

using namespace std;

namespace conversion {

	std::string change_mod_name(const std::string& name, int new_index)
	{
		std::ostringstream index_ostr;
		index_ostr << new_index;
		std::string index_str = index_ostr.str();

		std::string new_s = name;
		std::size_t pos_begin = new_s.find("MOD-") + strlen("MOD-");
		std::size_t pos_end = new_s.find('/', pos_begin);
		std::size_t lenght = pos_end - pos_begin;
		if(pos_begin != std::string::npos)
		{
			new_s.replace(pos_begin, lenght, index_str);
		}
		return new_s;
	}

}

class base_object
{
public:
	
	typedef std::string distname_type;

	base_object(const distname_type& dn) : distname(dn) {}
	distname_type distname;

	void set_distname(const distname_type& new_dn)
	{
		distname = new_dn;
	}

};

class im_storage 
{
public:

	struct object_holder
	{
		typedef boost::shared_ptr<base_object> handle_type;

		explicit object_holder(base_object* obj): my_object(obj) {}
		handle_type my_object;
	};

	struct hash : std::unary_function<object_holder*, size_t>
	{
		size_t operator()(const object_holder* oh) const
		{
			std::cout << "hash::operator() " << oh->my_object->distname << std::endl;
			boost::hash<base_object::distname_type> hasher;
			return hasher(oh->my_object->distname);
		}
	};

	struct object_holder_compare: std::binary_function<object_holder*, object_holder*, bool>
	{
		bool operator()(const object_holder* a, const object_holder* b) const
		{
			std::cout << "object_holder_compare: a " << a->my_object->distname << " == b " << b->my_object->distname << std::endl;
			return a->my_object->distname == b->my_object->distname;
		}
	};

	struct object_holder_str_compare: std::binary_function<base_object::distname_type, object_holder*, bool>
	{
		bool operator()(const base_object::distname_type& a, const object_holder* b) const
		{
			std::cout << "object_holder_str_compare: a " << a << " == b " << b->my_object->distname << std::endl;
			return a == b->my_object->distname;
		}
	};

	//-----------------------------------------------------------------------------------------------------------

	typedef boost::unordered_set<object_holder*, hash, object_holder_compare> im_container_type;

	im_container_type objects;

	bool object_exists(const base_object::distname_type& dn) const
	{
		std::cout << std::endl << "object_exists " << dn << std::endl;
		return find_object(dn) != objects.end();
	}

	im_container_type::const_iterator find_object(const base_object::distname_type& dn) const
	{
		return objects.find(dn, boost::hash<base_object::distname_type>(), object_holder_str_compare());
	}

	void add_holder_ptr(object_holder* oh)
	{
		objects.insert(oh);
	}

	bool add_object(std::auto_ptr<base_object>& obj)
	{
		std::cout << "add_object START" << std::endl;
		if (find_object(obj->distname) != objects.end())
		{
			std::cout << "object with distname = " << obj->distname.c_str() << " already present " << std::endl;
			return false;
		}

		object_holder* oh = new object_holder(obj.release());
		std::pair<im_container_type::iterator, bool> ins = objects.insert(oh);

		if (ins.second) {
			const base_object::distname_type& dn = oh->my_object->distname;
			std::cout << "added object " << dn.c_str() << std::endl;
			
		} else {
			std::cout << "add_object(): internal error" << std::endl;
			delete oh;
		}

		return ins.second;
	}

	bool validate_object(im_storage::im_container_type::const_iterator it, const base_object::distname_type& dn) const
	{
		if (it == objects.end()) {
			std::cout << "object " << dn << " not found" << std::endl;
			return false;
		}

		return true;
	}
		
	bool destroy_holder_ptr(const base_object::distname_type& dn)
	{
		im_container_type::iterator it = find_object(dn);

		if (! validate_object(it, dn)) {
			std::cout << "destroying object " << dn << " failed" << std::endl;
			return false;
		}
		objects.erase(it);
		return true;
	}

	bool destroy_object(const base_object::distname_type& dn)
	{
		im_container_type::iterator it = find_object(dn);

		if (! validate_object(it, dn)) {
			std::cout << "destroying object " << dn << " failed" << std::endl;
			return false;
		}
		objects.erase(it);
		return true;
	}

	bool change_mod_distname(const base_object::distname_type& curr_dn, int index)
	{
		std::cout << std::endl << "change_mod_distname from " << curr_dn << " to " << index << std::endl;

		if(object_exists(curr_dn))
		{
			im_container_type::iterator it_obj = find_object(curr_dn);


			object_holder* obj_h = *it_obj;

			destroy_holder_ptr(curr_dn);
			
			base_object::distname_type new_dist = conversion::change_mod_name(obj_h->my_object->distname, index);
			obj_h->my_object->set_distname(new_dist);

			add_holder_ptr(obj_h);
			
			std::cout << std::endl << "CHECK CHANGE------------------------------------------------" << std::endl;
			if (object_exists(new_dist.c_str()))
			{
				std::cout << "change_mod_distname succesfull to: " <<  new_dist.c_str() << std::endl;
			}
			else
			{
				std::cout << "change_mod_distname failure! Distname " <<  new_dist.c_str() << " not found" << std::endl;
			}
			std::cout << std::endl << "------------------------------------------------" << std::endl;
		}

		return true;
	}

	void print_objects()
	{
		std::cout << std::endl << "print_objects" << std::endl;
		for (im_container_type::const_iterator it = objects.begin(); it != objects.end(); ++it) {
					std::cout << "distname " <<  (*it)->my_object->distname << std::endl;
		}
	}

	void print_umap_details()
	{
		std::cout << std::endl;
		std::cout << "objects.bucket_count(): " << objects.bucket_count() << std::endl;
		std::cout << "objects.max_load_factor(): " << objects.max_load_factor() << std::endl;
		std::cout << "objects.load_factor(): " << objects.load_factor() << std::endl;

		for (unsigned  i = 0; i < objects.bucket_count(); ++i) {
			std::cout << "bucket " << i << " has size " << objects.bucket_size(i) << " and contains: " << std::endl;

			for(im_container_type::local_iterator it = objects.begin(i); it != objects.end(i); ++it) {

				const base_object::distname_type& dn = (*it)->my_object->distname;
				std::cout << dn << std::endl;
			}
		}
	}

	void print_hashes()
	{
		std::cout << std::endl;
		im_container_type::hasher hfunc = objects.hash_function(); // get a pointer to the function
				for(im_container_type::const_iterator it = objects.begin(); it != objects.end(); ++it) {
					std::cout << "hashes for " << (*it)->my_object->distname << " are: " << hfunc((*it)) << std::endl;
				}
	}

	void rehash_objects()
	{
		std::cout << std::endl << "rehash_objects" << std::endl;
		for (im_container_type::const_iterator it = objects.begin(); it != objects.end(); ++it) {
				//hash((it*)());
			//objects.hasher();
		}
		std::cout << std::endl << "rehash_objects end" << std::endl;
	}

};

int _tmain(int argc, _TCHAR* argv[])
{
	im_storage store;

	std::auto_ptr<base_object> obj1(new base_object("/MRBTS-1"));
	store.add_object(obj1);
	std::auto_ptr<base_object> obj2(new base_object("/MRBTS-1/SMOD-1"));
	store.add_object(obj2);
	std::auto_ptr<base_object> obj3(new base_object("/MRBTS-1/SMOD-1/CCU-1"));
	store.add_object(obj3);
	std::auto_ptr<base_object> obj4(new base_object("/MRBTS-1/SMOD-1/CCU-2"));
	store.add_object(obj4);

	store.print_objects();
	store.print_hashes();
	store.print_umap_details();

	store.change_mod_distname("/MRBTS-1/SMOD-1", 2);
	//store.objects.rehash(static_cast<std::size_t>(20));
	//store.print_hashes();
	store.change_mod_distname("/MRBTS-1/SMOD-1/CCU-1", 2);
	//store.print_hashes();
	store.change_mod_distname("/MRBTS-1/SMOD-1/CCU-2", 2);
	//store.print_hashes();

	//store.print_objects();

	std::cout << std::endl << "TEST------------------------------------------------" << std::endl;
	if( !store.object_exists("/MRBTS-1/SMOD-2") )
	{
		cout << "not found 1" << std::endl;
	}

	if( !store.object_exists("/MRBTS-1/SMOD-2/CCU-1") )
	{
		cout << "not found 2" << std::endl;
	}

	if( !store.object_exists("/MRBTS-1/SMOD-2/CCU-2") )
	{
		cout << "not found 3" << std::endl;
	}

	getchar();
	return 0;
}

